export default class DatePicker {
  /**
   * 
   * @param {String} el dom元素
   * @param {String} date 显示日期
   * @param {Function} clickCb 点击日期后的回调函数
   */
  constructor({ el, date=new Date(), clickCb = () => { } }) {
    // 获取dom元素
    // 当前页天数栏
    this.nowMonth = document.querySelector(`${el} .now-month`)
    // 下一页天数栏
    this.nextMonth = document.querySelector(`${el} .next-month`)
    // 小箭头
    this.arrow = document.querySelectorAll(`${el} .arrow`)
    // 整个盒子
    this.dateBox = document.querySelector(`${el} .dateBox`)

    this.date = date
    if(typeof(date) === "string"){
      this.date = new Date(this.date)
    }

    // 选择的日期
    this.chooseDay = []

    // 选择日期回调函数
    this.clickCb = clickCb

    this.showToday()

    // 根据当前年份月份渲染页面
    this.showPage()
    // 绑定切换按钮事件
    this.switched()
    // 绑定选定日期按钮
    this.choose()
  }

  // 渲染今天
  showToday() {
    this.todayStr = this.date.toLocaleDateString()
    const arr = this.todayStr.split("/")
    this.currentYear = arr[0] - 0
    this.currentMonth = arr[1] - 0
  }

  // 渲染到在页面
  showPage() {
    this.nowMonth.innerHTML = ""
    this.nextMonth.innerHTML = ""
    // 渲染前一页
    this.showNowMonth(this.currentYear, this.currentMonth, 0, this.nowMonth)
    let nextM = this.nextM(this.currentYear, this.currentMonth)
    // 渲染下一页
    this.showNowMonth(nextM.year, nextM.month, 1, this.nextMonth)
    // 渲染当前天
    this.arrow[0].nextElementSibling.innerHTML = `${this.currentYear}-${this.currentMonth < 10 ? "0" + this.currentMonth : this.currentMonth}`
    this.arrow[1].previousElementSibling.innerHTML = `${nextM.year}-${nextM.month < 10 ? "0" + nextM.month : nextM.month}`

    // 渲染已选中
    this.chooseShow()
  }

  // 动态渲染日历,传入一个月份,当前月
  /**
   * 
   * @param {String} year 年
   * @param {String} month 月
   * @param {String} next 是否渲染的是下个月
   * @param {String} ele 渲染的dom元素
   */
  showNowMonth(year, month, next, ele) {
    // 这个月的天数数组
    const dayArr = []
    // 三个月的天数数组
    const nextMonDay = []

    // 计算月份最后的一个
    let last = this.lastDay(year, month)
    // 计算星期几
    let week = this.getDayOfWeek(year, month, 1)

    // 当前月数的1号是星期几,就要向前追几天,星期日为0则不追
    let i = week

    let isTodaySty = ""

    // 计算上个月的最后一天
    let l
    // 上个月是哪年
    let y
    // 上个月是哪月
    let m
    if (month === 1) {
      y = year - 1
      m = 12
    }
    else {
      y = year
      m = month - 1
    }
    l = this.lastDay(y, m)
    while (i !== 0) {
      // 利用星期数,将需要渲染的上个月的天数放到数组里
      nextMonDay.unshift(l)
      l--
      i--
    }
    // 之后的天数渲染从1渲染到最后一天即可
    for (let j = 1; j <= last; j++) {
      dayArr.push(j)
    }
    if (next) {
      ele.innerHTML += nextMonDay.reduce((pre, res) => {
        return pre + `
        <li class="other-month">
          <span title="" class="dayStyle"></span>
        </li>
        `
      }, "")
    } else {
      ele.innerHTML += nextMonDay.reduce((pre, res) => {
        isTodaySty = ""
        if (`${y}/${m + 1}/${res}` === this.todayStr) {
          isTodaySty = "today-style"
        }
        return pre + `
        <li class="other-month">
          <span title="${y}-${m}-${res}" class="dayStyle ${isTodaySty}">${res}</span>
        </li>
        `
      }, "")
    }
    ele.innerHTML += dayArr.reduce((pre, res) => {
      isTodaySty = ""
      if (`${y}/${m + 1}/${res}` === this.todayStr) {
        isTodaySty = "today-style"
      }
      return pre + `
      <li class="current-month">
        <span title="${year}-${month}-${res}" class="currentDate ${isTodaySty} dayStyle">${res}</span>
      </li>
      `
    }, "")
  }

  // 选择方法
  choose() {
    this.dateBox.onclick = (e) => {
      if (e.target.title.length === 0) return
      const target = e.target
      // 事件委托
      if (target.classList.contains("dayStyle")) {
        const { dateStr, day } = this.swTime(target.title)
        // 将添加点击日期至数组中
        if (this.chooseDay.length === 2) {
          // 则将数组清空,说明用户需要重新选择
          this.chooseDay = []
        }
        // 如果两次的时间戳都一样的,说明用户点击了同一天,则也要把数组清除
        if (this.chooseDay[0] && this.chooseDay[0].dateStr === dateStr) {
          this.chooseDay = []
          this.chooseShow()
          return
        }
        this.chooseDay.push({ dateStr, day })
        this.chooseDay.sort((a, b) => a.dateStr - b.dateStr)
        this.clickCb(this.chooseDay[0], this.chooseDay[1])
        this.chooseShow()
      }
    }
  }

  // 渲染数组中的选中日期
  chooseShow() {
    [...this.nowMonth.children, ...this.nextMonth.children].some(res => {
      // 获取天数对象
      const day = res.children[0]

      // 遍历当前展示的所有天数转化为时间戳进行比较
      const { dateStr, day: dayed } = this.swTime(day.title)

      // 如果数组只有一个数据,就只需要一个点亮即可, 并且要清除所有亮起的天
      if (this.chooseDay.length <= 1) {
        day.classList.remove("selected-style")
        day.classList.remove("between")
        if (this.chooseDay[0] && this.chooseDay[0].dateStr === dateStr) {
          day.classList.add("selected-style")
        }
      }

      // 如果数组有两个数据
      if (this.chooseDay.length === 2) {
        // 判断哪些时间存在于两个两个时间戳的范围中,在范围中的全加上between样式
        // 排序数组this.chooseDay
        this.chooseDay.sort((a, b) => a - b)
        if (dateStr === this.chooseDay[0].dateStr || dateStr === this.chooseDay[1].dateStr) {
          day.classList.add("selected-style")
        }
        if (dateStr > this.chooseDay[0].dateStr && dateStr < this.chooseDay[1].dateStr) {
          day.classList.add("between")
        }
      }
    })
  }

  // 将2012-2-2转化为时间戳,方便之后进行大小的比较
  swTime(str) {
    if (!/^\d{4}-\d{1,2}-\d{1,2}$/.test(str)) return {
      // 无需处理的
      day: "",
      dateStr: ""
    }
    // 先转为数组后,再转化为时间对象
    const arr = str.split("-")
    // 时间戳
    const dateStr = (new Date(arr[0] - 0, arr[1] - 1, arr[2] - 0)).getTime()
    return {
      // 2020-2-2格式
      day: str,
      // 时间戳
      dateStr
    }
  }

  // 翻页方法
  switched() {
    this.arrow[0].onclick = () => {
      // 当前月份加1
      const next = this.prevM(this.currentYear, this.currentMonth)
      this.currentMonth = next.month
      this.currentYear = next.year
      this.showPage()
    }
    this.arrow[1].onclick = () => {
      // 当前月份加1
      const next = this.nextM(this.currentYear, this.currentMonth)
      this.currentMonth = next.month
      this.currentYear = next.year
      this.showPage()
    }
  }

  // 根据当前年月算出下个年月
  nextM(year, month) {
    if (month === 12) return {
      year: year + 1,
      month: month = 1
    }

    return {
      year: year,
      month: month + 1
    }
  }

  // 根据当前年月算出上个年月
  prevM(year, month) {
    if (month === 1) return {
      year: year - 1,
      month: month = 12
    }

    return {
      year: year,
      month: month - 1
    }
  }

  // 判断平年闰年
  isfullYeal(year) {
    if (year % 4 === 0 && year % 100 !== 0 || year % 400 === 0) {
      return true
    }
    return false
  }

  // 判断月份的最后一天
  lastDay(year, month) {
    // 月份选择
    switch (month) {
      case 1: {
        return 31
      }
      case 2: {
        if (this.isfullYeal(year)) return 29
        return 28
      }
      case 3: {
        return 31
      }
      case 4: {
        return 30
      }
      case 5: {
        return 31
      }
      case 6: {
        return 30
      }
      case 7: {
        return 31
      }
      case 8: {
        return 31
      }
      case 9: {
        return 30
      }
      case 10: {
        return 31
      }
      case 11: {
        return 30
      }
      case 12: {
        return 31
      }
    }
  }

  // 根据年月日判断星期几
  getDayOfWeek(year, month, day) {
    // 处理日期
    const date = new Date(year, month - 1, day)
    // 判断星期几
    const W = date.getDay()
    return W;
  }
}